﻿// This code is released under the BSD license.
namespace PugLib.Functional
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public delegate void K<T>(Action<T> c);

    public delegate Answer K<T, Answer>(Func<T, Answer> k);


    public static class Monads
    {
        public static Identity<T> ToIdentity<T>(this T value)
        {
            return new Identity<T>(value);
        }

        public static Identity<U> SelectMany<T, U>(this Identity<T> id, Func<T, Identity<U>> k)
        {
            return k(id.Value);
        }

        public static Identity<V> SelectMany<T, U, V>(this Identity<T> id, Func<T, Identity<U>> k, Func<T, U, V> s)
        {
            return s(id.Value, k(id.Value).Value).ToIdentity();
        }

        public static Maybe<T> ToMaybe<T>(this T value)
        {
            return new Maybe<T>(value);
        }

        public static Maybe<U> SelectMany<T, U>(this Maybe<T> m, Func<T, Maybe<U>> k)
        {
            if (!m.HasValue)
            {
                return Maybe<U>.Nothing;
            }
            return k(m.Value);
        }

        public static Maybe<V> SelectMany<T, U, V>(this Maybe<T> id, Func<T, Maybe<U>> k, Func<T, U, V> s)
        {
            return s(id.Value, k(id.Value).Value).ToMaybe();
        }

        public static IEnumerable<T> ToEnumerable<T>(this T value)
        {
            yield return value;
        }


        public static IEnumerable<U> SelectMany<T, U>(this IEnumerable<T> m, Func<T, IEnumerable<U>> k)
        {
            foreach (T x in m)
            {
                foreach (U y in k(x))
                {
                    yield return y;
                }
            }
        }

        public static K<T, Answer> ToContinuation<T, Answer>(this T value)
        {
            return c => c(value);
        }

        public static K<T> ToContinuation<T>(this T value)
        {
            return c => c(value);
        }

        public static K<U, Answer> SelectMany<T, U, Answer>(this K<T, Answer> m, Func<T, K<U, Answer>> k)
        {
            return c => m(x => k(x)(c));
        }


        public static K<T, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<U, Answer>>, K<T, Answer>> u)
        {
            return c => u(x => z => c(x))(c);
        }

        public static K<V, Answer> SelectMany<T, U, V, Answer>(this K<T, Answer> m, Func<T, K<U, Answer>> k, Func<T, U, V> s)
        {
            return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToContinuation<V, Answer>()));
        }


        public static K<U> SelectMany<T, U>(this K<T> m, Func<T, K<U>> k)
        {
            return c => m(x => k(x)(c));
        }

        public static K<V> SelectMany<T, U, V>(this K<T> m, Func<T, K<U>> k, Func<T, U, V> s)
        {
            return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToContinuation()));
        }

        public static K<U> Select<U, T>(this K<T> m, Func<T, U> k)
        {
            return c => m(x => c(k(x)));
        }
    }
}